<template>
<view class="content">
	<!-- 分数区域 -->
	<view class="text">
		<view class="score">分数：{{ score }}</view>
		<view class="eat">吃：{{ count }}</view>
		<view class="level">等级： {{ level }}</view>
	</view>
	<!-- 游戏区域 -->
	<view class="game-gird">
		<view class="line" v-for="(item, index) in gameGrid" :key="index">
			<view v-for="(grid, ins) in item" :key="ins" class="grid" :style="{ background: grid } "></view>
		</view>
	</view>

	<!-- 按钮区域 -->
	<view class="btn">
		<view class="ztks">
			<button @click="stopGame">暂停/开始</button>
		</view>
		<view class="direction">
			<button @click="moveTop">向上</button>
			<view class="lr">
				<button @click="moveLeft">向左</button>
				<button @click="moveRight">向右</button>
			</view>
			<button @click="moveBottom">向下</button>
		</view>
	</view>
</view>
</template>

<script>
	export default {
		data() {
			return {
				// 游戏区域大小
				row: 20,
				col: 20,

				gameGrid: [],
				gridBgColor: '#ccc',
				snakeColor: 'red',
				foodColor: 'purple',

				score: 0,
				count: 0,
				level: 1,

				// [行，列，行，列，行，列....]
				snakeList: [],
				foodGrid: [],

				timer: null,
				// 移动速度
				speedMin: 600,
				speed: '',
				driction: ''
			}
		},
		onLoad() {
			this.initFun()
		},
		mounted() {
			// 添加事件监听器
			document.addEventListener('keydown', this.handKeyDown);
		},
		beforeDestroy() {
			// 移除事件监听器
			document.removeEventListener('keydown', this.handKeyDown);
		},
		methods: {
			handKeyDown(event) {
				// console.log(event.key);
				switch (event.key) {
					case 'ArrowUp':
					case 'w':
						console.log('向上');
						this.moveTop();
					break;
					case 'ArrowDown':
					case 's':
						console.log('向下');
						this.moveBottom();
					break;
					case 'ArrowLeft':
					case 'a':
						console.log('向左');
						this.moveLeft();
					break;
					case 'ArrowRight':
					case 'd':
						console.log('向右');
						this.moveRight();
					break;
				}
			},
			initFun() {
				this.speed = this.speedMin
				this.score = 0
				this.count = 0
				this.level = 1
				
				// 默认第一步向左移动
				this.driction = 'left'

				this.initGrid()
				this.initSnake()
				this.initFood()
			},
			// 初始化游戏区域
			initGrid() {
				this.gameGrid = []
				for(let i=0; i<this.row; i++) {
					let line = []
					for(let j=0; j<this.col; j++) {
						// 每个格子的颜色
						line.push(this.gridBgColor)
					}
					this.gameGrid.push(line)
				}
			},
			// 渲染方块  type: 1渲染 0清除
			renderGrid(gridList, bgColor, type) {
				if(type==1) {
					for(let i=0; i<gridList.length-1; i=i+2) {
						let row = gridList[i]
						let col = gridList[i+1]
						// vue侦听二维数组： 改变二维数组其中一个值时， 数组改变了， 但是页面没有变化
						// this.gameGrid[row][col] = bgColor  // 不生效

						if(row>=0&&row<this.row&col>=0&&col<this.col) {
							this.$set(this.gameGrid[row], col, bgColor)
						}
					}
				}
				else {
					for(let i=0; i<gridList.length-1; i=i+2) {
						let row = gridList[i]
						let col = gridList[i+1]
						// vue侦听二维数组： 改变二维数组其中一个值时， 数组改变了， 但是页面没有变化
						// this.gameGrid[row][col] = this.gridBgColor  // 不生效
						if(row>=0&&row<this.row&col>=0&&col<this.col) {
							this.$set(this.gameGrid[row], col, this.gridBgColor)
						}
					}
				}
			},
			// 初始化蛇
			initSnake() {
				// [行，列，行，列，行，列....]
				this.snakeList = [19,17,19,18,19,19]

				this.renderGrid(this.snakeList, this.snakeColor, 1)
			},
			// 随机生成食物，但是不能在蛇身上
			initFood() {
				// 随机显示食物
				this.foodGrid = [Math.floor(Math.random()*this.row), Math.floor(Math.random()*this.col)]

				for(let i=0; i<this.snakeList.length-1; i=i+2) {
					if(this.snakeList[i]==this.foodGrid[0]&&this.snakeList[i+1]==this.foodGrid[1]) {
						this.initFood()
					}
				}
				this.renderGrid(this.foodGrid, this.foodColor, 1)
			},
			// 控制蛇头四个方向上移动
			moveLeft() {
				// 不能反方向移动
				if(this.driction == 'right') return
				this.driction = 'left'
				clearInterval(this.timer)
				this.timer = setInterval(() => {
					// 先清除再渲染
					this.renderGrid(this.snakeList, this.snakeColor, 0)
					// 如果下一个元素就是食物， 则食物插入蛇数组作为蛇头； 如果不是则继续走
					let bool = this.eat()
					if(!bool) {
						this.move()
						this.snakeList[1]--
					}
					this.renderGrid(this.snakeList, this.snakeColor, 1)

					// 每走完一步判断是否游戏结束
					this.isJudge()
				}, this.speed)
			},
			
			moveRight() {
				// 不能反方向移动
				if(this.driction == 'left') return
				this.driction = 'right'
				clearInterval(this.timer)
				this.timer = setInterval(() => {
					// 先清除再渲染
					this.renderGrid(this.snakeList, this.snakeColor, 0)
					// 如果下一个元素就是食物， 则食物插入蛇数组作为蛇头； 如果不是则继续走
					let bool = this.eat()
					if(!bool) {
						this.move()
						this.snakeList[1]++
					}
					this.renderGrid(this.snakeList, this.snakeColor, 1)

					// 每走完一步判断是否游戏结束
					this.isJudge()
				}, this.speed)
			},
			
			moveTop() {
				// 不能反方向移动
				if(this.driction == 'bottom') return
				this.driction = 'top'
				clearInterval(this.timer)
				this.timer = setInterval(() => {
					// 先清除再渲染
					this.renderGrid(this.snakeList, this.snakeColor, 0)
					// 如果下一个元素就是食物， 则食物插入蛇数组作为蛇头； 如果不是则继续走
					let bool = this.eat()
					if(!bool) {
						this.move()
						this.snakeList[0]--
					}
					this.renderGrid(this.snakeList, this.snakeColor, 1)

					// 每走完一步判断是否游戏结束
					this.isJudge()
				}, this.speed)
			},
			
			moveBottom() {
				// 不能反方向移动
				if(this.driction == 'top') return
				this.driction = 'bottom'
				clearInterval(this.timer)
				this.timer = setInterval(() => {
					// 先清除再渲染
					this.renderGrid(this.snakeList, this.snakeColor, 0)
					// 如果下一个元素就是食物， 则食物插入蛇数组作为蛇头； 如果不是则继续走
					let bool = this.eat()
					if(!bool) {
						this.move()
						this.snakeList[0]++
					}
					this.renderGrid(this.snakeList, this.snakeColor, 1)

					// 每走完一步判断是否游戏结束
					this.isJudge()
				}, this.speed)
			},
			// 蛇身跟着蛇头的轨迹运动，蛇身从最后一个元素开始， 一次赋值为前一个元素
			// 蛇身先全部赋值， 蛇头再动
			move() {
				// 第一个蛇头元素不需要赋值为前一个元素  i>0
				for(let i=this.snakeList.length-2; i>0; i=i-2) {
					this.snakeList[i] = this.snakeList[i-2]
					this.snakeList[i+1] = this.snakeList[i-1]
				}
			},
			// 判断蛇头是否撞墙或者与蛇身相撞：相撞则结束游戏
			isJudge() {
				let that = this
				if(this.snakeList[0]<0 || this.snakeList[0]>=this.row) {
					clearInterval(this.timer)
					uni.showModal({
						title: '游戏结束',
						content: '撞墙了...',
						confirmText: '重新开始',
						cancelText: '退出',
						confirmColor: '#0bb74b',
						success: function (res) {
							if (res.confirm) {
								that.initFun()
							} else if (res.cancel) {
								// 退出app
								plus.runtime.quit()
							}
						}
					})
					return false
				}
				else if(this.snakeList[1]<0 || this.snakeList[1]>=this.col) {
					clearInterval(this.timer)
					uni.showModal({
						title: '游戏结束',
						content: '撞墙了...',
						confirmText: '重新开始',
						cancelText: '退出',
						confirmColor: '#0bb74b',
						success: function (res) {
							if (res.confirm) {
								that.initFun()
							} else if (res.cancel) {
								// 退出app
								plus.runtime.quit()
							}
						}
					})
					return false
				}
				else{
					// 判断与蛇身是否相撞
					for(let i=this.snakeList.length-2; i>0; i=i-2) {
						if(this.snakeList[i]==this.snakeList[0]&&this.snakeList[i+1]==this.snakeList[1]) {
							clearInterval(this.timer)
							uni.showModal({
								title: '游戏结束',
								content: '撞到自己了...',
								confirmText: '重新开始',
								cancelText: '退出',
								confirmColor: '#0bb74b',
								success: function (res) {
									if (res.confirm) {
										that.initFun()
									} else if (res.cancel) {
										// 退出app
										plus.runtime.quit()
									}
								}
							})
							return false
						}
					}
				}
				return true
			},
			// 暂停/开始按钮
			stopGame() {
				if(this.timer) {
					clearInterval(this.timer)
					this.timer = ''
				}
				else {
					if(this.driction == 'left') {
						this.moveLeft()
					}
					else if(this.driction == 'right') {
						this.moveRight()
					}
					else if(this.driction == 'top') {
						this.moveTop()
					}
					else if(this.driction == 'bottom') {
						this.moveBottom()
					}
				}
			},
			// 吃食物: 判断食物是否在蛇移动位置的方向上， 在蛇移动的方向上且就在蛇头的下一个即将到达的位置上时，就把食物插入设数组的前面作为蛇头
			eat() {
				if(this.driction=='left') {
					if(this.foodGrid[0]==this.snakeList[0] && this.foodGrid[1]==(this.snakeList[1]-1)) {
						this.snakeList.unshift(...this.foodGrid)
						this.initFood()
						this.scoreFun()
						return true
					}
				}
				else if(this.driction=='right') {
					if(this.foodGrid[0]==this.snakeList[0] && this.foodGrid[1]==(this.snakeList[1]+1)) {
						this.snakeList.unshift(...this.foodGrid)
						this.initFood()
						this.scoreFun()
						return true
					}
				}
				else if(this.driction=='top') {
					if(this.foodGrid[1]==this.snakeList[1] && this.foodGrid[0]==(this.snakeList[0]-1)) {
						this.snakeList.unshift(...this.foodGrid)
						this.initFood()
						this.scoreFun()
						return true
					}
				}
				else if(this.driction=='bottom') {
					if(this.foodGrid[1]==this.snakeList[1] && this.foodGrid[0]==(this.snakeList[0]+1)) {
						this.snakeList.unshift(...this.foodGrid)
						this.initFood()
						this.scoreFun()
						return true
					}
				}
				return false
			},
			scoreFun() {
				this.count++
				this.score = this.score + this.level*10
				this.speed = this.speedMin - (this.level-1)*40  // 晋升一个等级，速度快40毫秒

				this.level = (Math.floor(this.count/10) + 1) <= 15 ? Math.floor(this.count/10) + 1 : 15  // 每吃10个晋升一个等级(最高等级是15)
			}
		}
	}
</script>

<style lang="scss" scoped>
page {
	overflow: auto;
	height: 100%;
}
.content {
	margin: 30rpx auto;
	width: 700rpx;
	// background-color: #eee;

	.text {
		display: flex;
		view {
			flex: 1;
			padding: 10rpx;
			margin-bottom: 10rpx;
		}
	}

	.game-gird {
		width: 700rpx;
		height: 700rpx;
		// background-color: red;

		.line {
			display: flex;
			justify-content: center;

			.grid {
				// 700/20
				width: 35rpx;
				height: 35rpx;
				border: 1rpx solid #eee;
				box-sizing: border-box;
			}
		}
	}

	.btn {
		margin-top: 20rpx;

		.ztks {
			width: 100%;
			height: 80rpx;
			display: flex;
			justify-content: center;

			button {
				width: 220rpx;
				font-size: 28rpx;
				height: 80rpx;
				line-height: 80rpx;
				margin: unset;
				background-color: #ccc;
			}
		}

		.direction {
			margin-top: 40rpx;
			
			button {
				width: 240rpx;
				font-size: 28rpx;
				height: 80rpx;
				line-height: 80rpx;
				background-color: #ccc;
			}

			.lr {
				display: flex;
				align-items: center;
				margin: 20rpx 0;
			}
		}
	}
}
</style>
